/*
* @file llm.cpp
* @brief LLM class implementation
* @note This file contains the implementation of the LLM class.
* @author feiniao
* @version 1.0
* @date 2025-5-14
* It includes the constructor, destructor, and process function.
* The process function handles the LLM output and sends it to the LLM API.
*/

#include "llm.h"

/*
* note: embeded certificate
* @brief This is the certificate for the LLM API.
*/
extern const char my_django_cert_pem_start[] asm("_binary_my_django_cert_pem_start");
extern const char my_django_cert_pem_end[] asm("_binary_my_django_cert_pem_end");


/*
* @brief LLM class constructor
* @note This function initializes the LLM object.
* It creates an instance of the LLM class.
* The constructor is called when the LLM object is created.
*/
LLM::LLM() {
    ESP_LOGI(TAG, "Create LLM class ok");
}


/*
* @brief LLM class destructor
* @note This function is called when the LLM object is destroyed.
* It cleans up any resources used by the LLM class.
* The destructor is responsible for releasing any memory or resources allocated during the lifetime of the class.
*/
LLM::~LLM() {
    ESP_LOGI(TAG, "Destroyed LLM class ok");
}


/*
* @brief Event handler for LLM HTTP client
* @param evt Pointer to the HTTP client event
* @return ESP_OK on success, or an error code on failure
* @note This function handles various events from the HTTP client.
* It processes the received data and parses the JSON response.
* It accumulates data into a static buffer and resets the buffer when the event is finished.
*/
static esp_err_t LLM_event_handler(esp_http_client_event_t* evt) {
    static char response_buffer[MAX_RESPONSE_LEN];  // 静态缓冲区存储响应
    static size_t response_len = 0;                 // 当前数据长度
    switch (evt->event_id)
    {
        case HTTP_EVENT_ON_DATA: {
            // 累积数据到缓冲区
            if (response_len < MAX_RESPONSE_LEN - 1) {
                size_t to_copy = MIN(evt->data_len, MAX_RESPONSE_LEN - response_len - 1);
                memcpy(response_buffer + response_len, evt->data, to_copy);
                response_len += to_copy;
            }
            break;
        }
        case HTTP_EVENT_ON_FINISH: {
            response_buffer[response_len] = '\0';

            cJSON *response_json = cJSON_Parse(response_buffer);
    
            if (response_json) {
                cJSON *result = cJSON_GetObjectItem(response_json, "result");
                if (result && cJSON_IsArray(result)) {
                    cJSON *first_result = cJSON_GetArrayItem(result, 0);
                    if (cJSON_IsString(first_result) && first_result->valuestring) {
                    char* text_copy = strdup(first_result->valuestring);
                    if (xQueueSend(llm_output_queue, &text_copy, pdMS_TO_TICKS(100)) != pdTRUE) {
                        free(text_copy);
                        }
                    }
                }
                cJSON_Delete(response_json);
            } else {
                ESP_LOGE(TAG, "JSON解析失败");
            }   
            response_len = 0;
            break;
        }
        case HTTP_EVENT_DISCONNECTED:
            response_len = 0;  // 重置以应对重连
            break;
        default:
            break;
    }
    return ESP_OK;
}


/*
* @brief Process function for LLM
* @note This function is responsible for processing the LLM output.
* It receives data from the STT output queue, constructs a JSON payload,
* and sends an HTTP POST request to the LLM API.
* It handles the response and cleans up any allocated resources.
* The function is called when the LLM object is in use.
*/

void LLM::process() {

    char* stt_text = nullptr;
    if (xQueueReceive(stt_output_queue, &stt_text, portMAX_DELAY) != pdTRUE) {
        ESP_LOGE(TAG, "Failed to receive data from stt_output_queue");
        return;
    }

    if (stt_text == nullptr || strlen(stt_text) == 0) {
        ESP_LOGE(TAG, "Received empty text from llm_output_queue");
        free(stt_text); // 确保释放可能分配的内存
        return;
    }

    ESP_LOGI(TAG, "stt_output_queue: %s", stt_text);

    cJSON *messages = cJSON_CreateObject();

    cJSON_AddStringToObject(messages, "user_input", stt_text);

    char* payload = cJSON_PrintUnformatted(messages);
 
    ESP_LOGI(TAG, "payload : %s", payload);

    esp_http_client_config_t config = {
        .url = LLM_API_URL,
        .cert_pem = my_django_cert_pem_start,
        .method = HTTP_METHOD_POST,
        .timeout_ms = 100000,
        .event_handler = LLM_event_handler,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);

    esp_http_client_set_header(client, "Content-Type", "application/json");

    esp_http_client_set_post_field(client, payload, strlen(payload));

    esp_err_t err = esp_http_client_perform(client);

    if (err == ESP_OK) {
        ESP_LOGI(TAG, "Status: %d", esp_http_client_get_status_code(client));
    }

    cJSON_Delete(messages);
    free(payload);
    free(stt_text);
    esp_http_client_cleanup(client);
}
